package org.flexthinker.binding.bean
{
    import org.flexthinker.binding.bean.BindingGroupInternalListener;

    /**
     * <code>BindingGroup</code> allows you to create a group of <code>Bindings</code>
     * and operate and/or track state changes to the <code>Bindings</code> as a group.
     */
    public class BindingGroup
    {
        /**
         * List of listeners that have been registered to this <code>BindingGroup</code>.
         * The listeners will be called automatically, when any of the <code>Bindings</code>
         * attached to this <code>BindingGroup</code> will dispatch a notification.
         */
        protected var registeredListeners:Vector.<BindingListener> = new Vector.<BindingListener>();

        private var defaultBindingListener:BindingListener;

        internal var bound:Vector.<Binding> = new Vector.<Binding>();

        internal var unbound:Vector.<Binding> = new Vector.<Binding>();

        public function BindingGroup()
        {
        }

        public function addBinding(binding:Binding):void
        {
            if (binding == null)
            {
                throw new ArgumentError("Can't add a null Binding");
            }

            if (binding.isManaged)
            {
                throw new ArgumentError("Can't add a managed Binding");
            }

            if ( bound.indexOf(binding) >= 0 || unbound.indexOf(binding) >= 0 )
            {
                throw new ArgumentError("Group already contains this binding");
            }

            binding.addBindingListener( getDefaultBindingListener() );

            if ( binding.isBound )
            {
                bound.push(binding);
            }
            else
            {
                unbound.push(binding);
            }

        }

        public function removeBinding(binding:Binding):void
        {
            if ( binding == null)
            {
                throw new ArgumentError("Can't add a null Binding");
            }

            binding.removeBindingListener( getDefaultBindingListener() );

            var idx:int = -1;

            if ( binding.isBound)
            {
                idx = bound.indexOf(binding);
                if ( idx >= 0 )
                {
                    bound.splice( idx,  1);
                }
            }
            else
            {
                idx = unbound.indexOf(binding);
                if ( idx >=0 )
                {
                    unbound.splice(idx,  1);
                }
            }
        }

        public function getBindings():Vector.<Binding>
        {
            return bound.concat(unbound);
        }

        public function addBindingListener(listener:BindingListener):void
        {
            if (listener == null)
            {
                return;
            }

            if (registeredListeners.indexOf(listener) < 0)
            {
                registeredListeners.push(listener);
            }
        }

        public function removeBindingListener(listener:BindingListener):void
        {
            if (listener == null)
            {
                return;
            }
            var idx:int = registeredListeners.indexOf(listener);
            if (idx >= 0)
            {
                registeredListeners.splice(idx, 1);
            }
        }

        public function getBindingListeners():Vector.<BindingListener>
        {
            return registeredListeners;
        }

        /**
         * Calls <code>bind</code> on all unbound bindings in the group.
         * <p>
         *     New <code>Bindings</code> added to this group won't be automatically bounded,
         *     after calling this method. You would still need
         *     to call again <code>bind()</code> in order to bind the newly added <code>Binding</code>.
         *     </p>
         */
        public function bind():void
        {
            var unboundedBindings:Vector.<Binding> = unbound.concat();
            for each ( var binding:Binding in unboundedBindings )
            {
                binding.bind();
            }
        }

        /**
         * Calls <code>unbind</code> on all unbound bindings in the group.
         * <p>
         *     New <code>Bindings</code> added to this group won't be automatically unbounded,
         *     after calling this method.You would still need
         *     to call again <code>unbind()</code> in order to unbind the newly added <code>Binding</code>.
         *     </p>
         */
        public function unbind():void
        {
            var boundedBindings:Vector.<Binding> = bound.concat();
            for each ( var binding:Binding in boundedBindings )
            {
                binding.unbind();
            }
        }


        // ------------------
        // internal methods
        // ------------------
        private function getDefaultBindingListener():BindingListener
        {
            if (! defaultBindingListener)
            {
                defaultBindingListener = new BindingGroupInternalListener(this);
            }

            return defaultBindingListener;
        }

    }
}









